/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Portions created by the Initial Developer are
 * Copyright (C) 2012 the Initial Developer.
 * All Rights Reserved.
 *
 * The Original Code is "http://semi-frame.googlecode.com/svn/trenk/".
 *
 * The Initial Developer of the Original Code is the author below.
 */
/**
 * @file Controller.cpp
 * @brief auto generated.
 * @author mornongcloud.
 * @date 2012/03/11
 */

#include "semi/Controller.h"

namespace semi {

Controller::Controller() {
}

Controller::~Controller() {
	// Auto-generated destructor stub
}

Param Controller::doProcess(const std::string taskId, int retry) {

	this->TaskId = std::string(taskId.c_str());
	this->getLogger();

	this->Retry = retry;

	this->mCrnt.init(this->TaskId);
	this->mCrnt.setValid();

	semi::Param params;
	memset(&params, 0, sizeof(semi::Param));
	int siz = taskId.length();
	if (sizeof(params.task_id) < siz)
		siz = sizeof(params.task_id);
	strncpy(params.task_id, taskId.c_str(), siz);
	params.success = true;

	try {

		params = this->preProcess(params);

		if (params.success)
			params = this->commonProcess(params, retry);
		//		std::cout << "Controller::commonProcess end" << std::endl;

		if (params.success)
			params = this->endProcess(params);

		this->releaseLocks();

		return params;
	}
	// Exception for logic.
	catch (std::logic_error lx) {
		this->loggingLogicError(lx);
	}
	// Exception for logic.
	catch (std::logic_error* lxp) {
		this->loggingLogicErrorP(lxp);
	}
	// Standard exception.
	catch (const std::exception& ex) {

		this->logger.warn("Exception thrown" + std::string(ex.what()));
	}

	this->releaseLocks();

	params.success = false;
	return params;
}

semi::Param Controller::commonProcess(semi::Param params, int retry) {

	Param retn = params;

	// event listener
	ipc::MessageQue que(this->TaskId);
	semi::TimerMessage timer;

	conf::TasksConfig cnfgr;

	// application loop
	this->Retry = retry;
	this->retryCount = retry;
	this->onRetry = false;

	while (true) {

		conf::TasksConfig::TaskConfig cnf = cnfgr.getConfig(this->TaskId); // get last
		conf::TimerConfig timconf(cnf);

		if (!this->mCrnt.isValid()) {
			this->logger.info("Controller of " + TaskId + " shutdown!");
			break;
		}

		try {

			if (retryCount < this->Retry)
				onRetry = true;
			else
				onRetry = false;

			if (!onRetry) {
				retryCount = this->Retry;

				// delay send or not
				timer.send(cnf);

				if (this->logger.isTraceEnabled())
					this->logger.trace("Controller of " + TaskId
							+ " process wait.");

				// !!! wait event !!! //
				retn.size = que.receive();
				if (0 < retn.size) {
					retn.message = (char*) malloc(retn.size);
					memcpy(retn.message, (char*) que.message, retn.size);
				} else
					retn.message = 0;

				if (!this->mCrnt.isValid()) { // switch invalid for wait
					this->logger.info("Controller of " + TaskId
							+ " shutdown! receive:" + std::string(retn.message,
							retn.size));
					std::cout << "#### Shutdown ####" << std::endl;
					que.terminate();
					break;
				}

				if (this->logger.isTraceEnabled())
					this->logger.trace("message dump:"
							+ util::LogUtil::HexString(retn.message, retn.size));

				if (this->logger.isInfoEnabled())
					this->logger.info("Controller of " + TaskId
							+ " receive message:" + std::string(
							(char*) retn.message, retn.size));
			}
			// on retry
			else {
				if (this->logger.isInfoEnabled()) {
					std::stringstream ss;
					ss << "Controller::commonProcess this->Retry:";
					ss << (retryCount + 1);
					ss << "/";
					ss << this->Retry;
					this->logger.info(ss.str(), __func__, __FILE__);
				}
				retryCount++;
			}

			if (this->logger.isTraceEnabled())
				this->logger.trace("Process of " + TaskId + " start.");

			this->mCrnt.setLastTimestamp(util::DateUtil().getTime());

			// !!! user task !!! //
			retn = this->process(retn);
			if (0 < retn.message)
				free(retn.message);

			if (this->logger.isTraceEnabled())
				this->logger.trace("Process of " + TaskId + " returned.");
		}
		// logic exception
		catch (std::logic_error lx) {
			loggingLogicError(lx);

			if (!this->onRetry && this->Retry == retryCount)
				this->retryCount = 0;
		}
		// logic exception
		catch (std::logic_error* lxp) {
			loggingLogicErrorP(lxp);

			if (!this->onRetry && this->Retry == retryCount)
				this->retryCount = 0;
		}

		if (this->retryCount < this->Retry)
			continue;
	}

	//	std::cout << "Controller::commonProcess end" << std::endl;

	return retn;
}

void Controller::releaseLocks() {

	// log files
	bool use = false;
	logg::Logger logg = this->getLogger();
	util::Strings ids = CurrentTasks::getCurrentTaskIds();
	for (util::Strings::iterator iter = ids.begin(); iter != ids.end(); iter++) {
		std::string id = (std::string) *iter;
		if (id != this->TaskId) {
			conf::Configurator::LogConfig conf =
					conf::Configurator().getLogConfig(id);
			if (std::string(conf.log_path) == logg.printer.file_path) {
				use = true; // under use
				break;
			}
		}
	}
	if (!use) {
		this->logger.debug("Controller of " + TaskId
				+ " release log file's lock " + logg.printer.file_path);

		logg.releaseLock();
	}

	// current mmap
	this->mCrnt.clear();
}

Param Controller::preProcess(Param params) {
	if (this->logger.isTraceEnabled())
		this->logger.trace("PreProcess of " + this->TaskId + " is empty.");
	return params;
}

Param Controller::process(Param params) {
	if (this->logger.isDebugEnabled())
		this->logger.debug("Process of " + this->TaskId + " is empty.");
	return params;
}

Param Controller::endProcess(Param params) {
	if (this->logger.isTraceEnabled())
		this->logger.trace("EndProcess of " + this->TaskId + " is empty.");
	return params;
}

void Controller::loggingLogicError(std::logic_error lx) {

	this->logger.error("Exception thrown for Logic. " + std::string(lx.what()));
}

void Controller::loggingLogicErrorP(std::logic_error* lxp) {

	this->logger.error("Exception thrown for Logic. "
			+ std::string(lxp->what()));
}

int Controller::send(std::string taskId, std::string message, int sleepsec) {

	// check loaded
	CurrentTasks mtsk(taskId);
	if (!mtsk.isValid())
		return 0;

	int pid = 0;
	if (0 == (pid = fork())) {// TODO make class?
		if (0 < sleepsec)
			sleep(sleepsec);

		ipc::MessageQue que(this->TaskId);
		//		Param msg;
		//		strncpy(msg.task_id, taskId.c_str(), taskId.length());
		//		msg.message = (char*) message.c_str();
		//		msg.size = message.length();
		int ret = que.send(message);

		exit(0);
	}
	return pid;
}

logg::Logger Controller::getLogger() {

	if (0 == &this->logger.Name || 0 == this->logger.Name.length())
		this->logger = logg::Logger::getLogger(this->TaskId);
	return this->logger;
}

std::string Controller::getTaskId() {
	return this->TaskId;
}

}
